#include "stdafx.h"
#include "TerrainMesh.h"
#include "gl.h"

//-------------------------------------------------------------------------
// constructors
//-------------------------------------------------------------------------

CTerrainMesh::CTerrainMesh()
{
	m_fTStripVertices = 0;
	m_fTStripNormals = 0;
	m_fMinHeight = 0.0f;
	m_fMaxHeight = 75.0f;	// default height scaling
	m_fMeshSpacing = 1.0f;
}

//-------------------------------------------------------------------------
// destructor
//-------------------------------------------------------------------------

CTerrainMesh::~CTerrainMesh()
{
	m_vVertexCoords.~vector< vector< vector3f > >();
	m_vVertexNormals.~vector< vector< vector3f > >();
	if (m_fTStripVertices != 0)
	{
		delete m_fTStripVertices;
		m_fTStripVertices = 0;
	}
	if (m_fTStripNormals != 0)
	{
		delete m_fTStripNormals;
		m_fTStripNormals = 0;
	}
}

//-------------------------------------------------------------------------
// overloaded operators
//-------------------------------------------------------------------------

//-------------------------------------------------------------------------
// functions
//-------------------------------------------------------------------------

void CTerrainMesh::DrawMesh()
{
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_NORMAL_ARRAY);

	glFrontFace(GL_CW);

	glColor3f(1.0f, 1.0f, 1.0f);

	// Loop through the triangle strips

	float *pVertices;
	float *pNormals;
	for (unsigned int i=0; i<m_nRows-1; i++) // skip last row
	{
		pVertices = m_fTStripVertices->m_fArray[i];
		pNormals = m_fTStripNormals->m_fArray[i];
		glVertexPointer(3, GL_FLOAT, 0, pVertices);
		glNormalPointer(GL_FLOAT, 0, pNormals);
		glDrawArrays(GL_TRIANGLE_STRIP, 0, m_nVerticesPerStrip);
	}

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
}

//-------------------------------------------------------------------------

void CTerrainMesh::CreateMesh(CHeightmap &hmap)
{
	// Feed the data from the heightmap to the vertex array

	int index = 0;
	int	nBytesPerPixel = hmap.m_HeightValues.m_nBytesPerPixel;
	m_nRows = hmap.m_HeightValues.m_pTGAFileHeader->Image.Height;
	m_nCols = hmap.m_HeightValues.m_pTGAFileHeader->Image.Width;
	m_nVerticesPerStrip = hmap.m_HeightValues.m_pTGAFileHeader->Image.Width * 2;

	// Flush out old data

	m_vVertexCoords.clear();
	m_vVertexCoords.resize(m_nRows);

	if (m_fTStripVertices != 0)
	{
		delete m_fTStripVertices;
		m_fTStripVertices = 0;
	}
	m_fTStripVertices = new CArray2D(m_nRows, m_nVerticesPerStrip * 3);

	if (m_fTStripNormals != 0)
	{
		delete m_fTStripNormals;
		m_fTStripNormals = 0;
	}
	m_fTStripNormals = new CArray2D(m_nRows, m_nVerticesPerStrip * 3);

	// Load image data bytes into array

	float fAvoidDiv = 1.0f / (255.0f - 0.0f); // default min and max height

	for (unsigned int i=0; i<m_nRows; i++)
	{
		for (unsigned int j=0; j<m_nCols; j++)
		{
			vector3f vTemp; // map on X-Z plane and center mesh on origin
			vTemp.x = (float)-1 * (m_nCols/2) + j;
			vTemp.y = (((float)hmap.m_HeightValues.m_pImageData[index] - m_fMinHeight) * fAvoidDiv) * m_fMaxHeight;
			vTemp.z = (float)(m_nRows/2) - i;
			m_vVertexCoords[i].push_back(vTemp);
			index+=nBytesPerPixel;
		}
	}

	// Compute vertex normals

	ComputeNormals();

	// Compute new triangle strips

	CreateTStrips();
}

//-------------------------------------------------------------------------

void CTerrainMesh::AdjustMesh(float fNewMaxHeight, float fNewMeshSpacing)
{
	// Compute scaling factors

	float fAvoidDiv = 1.0f / (m_fMaxHeight - m_fMinHeight);

	for (unsigned int i=0; i<m_nRows; i++)
	{
		for (unsigned int j=0; j<m_nCols; j++)
		{
			// Adjust heights
			float fOldHeight = m_vVertexCoords[i][j].y;
			m_vVertexCoords[i][j].y = ((fOldHeight - m_fMinHeight) * fAvoidDiv) * fNewMaxHeight;

			// Adjust spacing
			m_vVertexCoords[i][j].x = (m_vVertexCoords[i][j].x / m_fMeshSpacing) * fNewMeshSpacing;
			m_vVertexCoords[i][j].z = (m_vVertexCoords[i][j].z / m_fMeshSpacing) * fNewMeshSpacing;
		}
	}

	// Assign new attributes

	m_fMaxHeight = fNewMaxHeight;
	m_fMeshSpacing = fNewMeshSpacing;

	// Compute vertex normals

	ComputeNormals();

	// Compute new triangle strips

	CreateTStrips();
}

//-------------------------------------------------------------------------

void CTerrainMesh::ComputeNormals()
{
	// Flush out old data

	m_vVertexNormals.clear();
	m_vVertexNormals.resize(m_nRows);

	// Assign new normals

	for (unsigned int i=0; i<m_nRows; i++)
	{
		for (unsigned int j=0; j<m_nCols; j++)
		{
			// Each corner vertex is a part of 1 triangle

			if (i == 0 && j == 0) // top left corner
			{
				vector3f vLeg1 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i+1][j]);
				vector3f vLeg2 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i][j+1]);
				vector3f vNormal = CrossProduct(vLeg2, vLeg1); vNormal.Normalize(); // clockwise cross product
				m_vVertexNormals[i].push_back(vNormal);
				continue;
			}
			if (i == 0 && j == m_nCols-1) // top right corner
			{
				vector3f vLeg1 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i][j-1]);
				vector3f vLeg2 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i+1][j]);
				vector3f vNormal = CrossProduct(vLeg2, vLeg1); vNormal.Normalize();
				m_vVertexNormals[i].push_back(vNormal);
				continue;
			}
			if (i == m_nRows-1 && j == 0) // bottom left corner
			{
				vector3f vLeg1 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i][j+1]);
				vector3f vLeg2 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i-1][j]);
				vector3f vNormal = CrossProduct(vLeg2, vLeg1); vNormal.Normalize();
				m_vVertexNormals[i].push_back(vNormal);
				continue;
			}
			if (i == m_nRows-1 && j == m_nCols-1) // bottom right corner
			{
				vector3f vLeg1 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i-1][j]);
				vector3f vLeg2 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i][j-1]);
				vector3f vNormal = CrossProduct(vLeg2, vLeg1); vNormal.Normalize();
				m_vVertexNormals[i].push_back(vNormal);
				continue;
			}

			// Each edge vertex is a part of 3 triangles

			if (i == 0 && j > 0) // top edge
			{
				vector3f vLeg1 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i][j-1]);
				vector3f vLeg2 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i+1][j-1]);
				vector3f vLeg3 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i+1][j]);
				vector3f vLeg4 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i][j+1]);
				vector3f vNormal1 = CrossProduct(vLeg2, vLeg1);
				vector3f vNormal2 = CrossProduct(vLeg3, vLeg2);
				vector3f vNormal3 = CrossProduct(vLeg4, vLeg3);
				vector3f vNormal = vNormal1 + vNormal2 + vNormal3; vNormal.Normalize();
				m_vVertexNormals[i].push_back(vNormal);
				continue;
			}

			if (i > 0 && j == 0) // left edge
			{
				vector3f vLeg1 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i+1][j]);
				vector3f vLeg2 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i][j+1]);
				vector3f vLeg3 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i-1][j+1]);
				vector3f vLeg4 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i-1][j]);
				vector3f vNormal1 = CrossProduct(vLeg2, vLeg1);
				vector3f vNormal2 = CrossProduct(vLeg3, vLeg2);
				vector3f vNormal3 = CrossProduct(vLeg4, vLeg3);
				vector3f vNormal = vNormal1 + vNormal2 + vNormal3; vNormal.Normalize();
				m_vVertexNormals[i].push_back(vNormal);
				continue;
			}

			if (i > 0 && j == m_nCols-1) // right edge
			{
				vector3f vLeg1 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i-1][j]);
				vector3f vLeg2 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i][j-1]);
				vector3f vLeg3 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i+1][j-1]);
				vector3f vLeg4 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i+1][j]);
				vector3f vNormal1 = CrossProduct(vLeg2, vLeg1);
				vector3f vNormal2 = CrossProduct(vLeg3, vLeg2);
				vector3f vNormal3 = CrossProduct(vLeg4, vLeg3);
				vector3f vNormal = vNormal1 + vNormal2 + vNormal3; vNormal.Normalize();
				m_vVertexNormals[i].push_back(vNormal);
				continue;
			}

			if (i == m_nRows-1 && j > 0) // bottom edge
			{
				vector3f vLeg1 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i][j+1]);
				vector3f vLeg2 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i-1][j+1]);
				vector3f vLeg3 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i-1][j]);
				vector3f vLeg4 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i][j-1]);
				vector3f vNormal1 = CrossProduct(vLeg2, vLeg1);
				vector3f vNormal2 = CrossProduct(vLeg3, vLeg2);
				vector3f vNormal3 = CrossProduct(vLeg4, vLeg3);
				vector3f vNormal = vNormal1 + vNormal2 + vNormal3; vNormal.Normalize();
				m_vVertexNormals[i].push_back(vNormal);
				continue;
			}

			// Each inner vertex is a part of 6 triangles

			{
				vector3f vLeg1 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i][j+1]);
				vector3f vLeg2 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i-1][j+1]);
				vector3f vLeg3 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i-1][j]);
				vector3f vLeg4 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i][j-1]);
				vector3f vLeg5 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i+1][j-1]);
				vector3f vLeg6 = (m_vVertexCoords[i][j]).Vector(m_vVertexCoords[i+1][j]);
				vector3f vNormal1 = CrossProduct(vLeg2, vLeg1);
				vector3f vNormal2 = CrossProduct(vLeg3, vLeg2);
				vector3f vNormal3 = CrossProduct(vLeg4, vLeg3);
				vector3f vNormal4 = CrossProduct(vLeg5, vLeg4);
				vector3f vNormal5 = CrossProduct(vLeg6, vLeg5);
				vector3f vNormal6 = CrossProduct(vLeg1, vLeg6);
				vector3f vNormal = vNormal1 + vNormal2 + vNormal3 + vNormal4 + vNormal5 + vNormal6; vNormal.Normalize();
				m_vVertexNormals[i].push_back(vNormal);
			}
		}
	}
}

//-------------------------------------------------------------------------

void CTerrainMesh::DrawNormals()
{
	for (unsigned int i=0; i<m_nRows; i++)
	{
		for (unsigned int j=0; j<m_nCols; j++)
		{
			vector3f vertex, normal;
			vertex = m_vVertexCoords[i][j];
			normal = m_vVertexNormals[i][j];
			normal += vertex;
			glColor3f(0.0f, 1.0f, 0.0f);
			glBegin(GL_LINES);
				glVertex3f(vertex.x, vertex.y, vertex.z);
				glVertex3f(normal.x, normal.y, normal.z);
			glEnd();
		}
	}
}

//-------------------------------------------------------------------------

void CTerrainMesh::CreateTStrips()
{
	// Create triangle strips

	int nFloat = 0;
	for (unsigned int i=0; i<m_nRows-1; i++) // skip last row
	{
		for (unsigned int j=0; j<m_nCols; j++)
		{
			// grab top strip vertex
			m_fTStripVertices->m_fArray[i][nFloat] = m_vVertexCoords[i][j].x;
			m_fTStripVertices->m_fArray[i][nFloat+1] = m_vVertexCoords[i][j].y;
			m_fTStripVertices->m_fArray[i][nFloat+2] = m_vVertexCoords[i][j].z;

			m_fTStripNormals->m_fArray[i][nFloat] = m_vVertexNormals[i][j].x;
			m_fTStripNormals->m_fArray[i][nFloat+1] = m_vVertexNormals[i][j].y;
			m_fTStripNormals->m_fArray[i][nFloat+2] = m_vVertexNormals[i][j].z;

			// grab bottom strip vertex
			m_fTStripVertices->m_fArray[i][nFloat+3] = m_vVertexCoords[i+1][j].x;
			m_fTStripVertices->m_fArray[i][nFloat+4] = m_vVertexCoords[i+1][j].y;
			m_fTStripVertices->m_fArray[i][nFloat+5] = m_vVertexCoords[i+1][j].z;

			m_fTStripNormals->m_fArray[i][nFloat+3] = m_vVertexNormals[i+1][j].x;
			m_fTStripNormals->m_fArray[i][nFloat+4] = m_vVertexNormals[i+1][j].y;
			m_fTStripNormals->m_fArray[i][nFloat+5] = m_vVertexNormals[i+1][j].z;

			nFloat += 6;
		}
		nFloat = 0;
	}

}